---
title: "Radio"
description: "Radio Group allow users to select a single option from a list of mutually exclusive options"
---

import {radioGroupContent} from "@/content/components/radio-group";

# Radio group

Radio Group allow users to select a single option from a list of mutually exclusive options.

<ComponentLinks component="radiogroup" reactAriaHook="useRadioGroup" npm="radio" styles="radio" source="radio" />

---

<CarbonAd/>

## Installation

<PackageManagers
  showGlobalInstallWarning
  commands={{
    cli: "npx heroui-cli@latest add radio",
    npm: "npm install @heroui/radio",
    yarn: "yarn add @heroui/radio",
    pnpm: "pnpm add @heroui/radio",
    bun: "bun add @heroui/radio"
  }}
/>

## Import

<ImportTabs
  commands={{
    main: 'import {RadioGroup, Radio} from "@heroui/react";',
    individual: 'import {RadioGroup, Radio} from "@heroui/radio";',
  }}
/>

## Usage

<CodeDemo title="Usage" files={radioGroupContent.usage} />

### Disabled

<CodeDemo title="Disabled" files={radioGroupContent.disabled} />

### Default Value

<CodeDemo title="Default Value" files={radioGroupContent.defaultValue} />

### With Description

<CodeDemo title="With Description" files={radioGroupContent.withDescription} />

### Horizontal

<CodeDemo title="Horizontal" files={radioGroupContent.horizontal} />

### Controlled

You can use the `value` and `onValueChange` properties to control the radio input value.

<CodeDemo title="Controlled" files={radioGroupContent.controlled} />

> **Note**: HeroUI `Radio` also supports native events like `onChange`, useful for form libraries
> such as [Formik](https://formik.org/) and [React Hook Form](https://react-hook-form.com/).

### Invalid

<CodeDemo title="Invalid" files={radioGroupContent.invalid} />

## Slots

- RadioGroup Slots

  - **base**: Radio group root wrapper, it wraps the label and the wrapper.
  - **wrapper**: Radio group wrapper, it wraps all Radios.
  - **label**: Radio group label, it is placed before the wrapper.
  - **description**: Description slot for the radio group.
  - **errorMessage**: Error message slot for the radio group.

- Radio Slots
  - **base**: Radio root wrapper, it wraps all elements.
  - **wrapper**: Radio wrapper, it wraps the control element.
  - **hiddenInput**: The hidden input element that is used to handle the radio state.
  - **labelWrapper**: Label and description wrapper.
  - **label**: Label slot for the radio.
  - **control**: Control element, it is the circle element.
  - **description**: Description slot for the radio.

### Custom Styles

You can customize the `RadioGroup` and `Radio` component by passing custom Tailwind CSS classes to the component slots.

<CodeDemo title="Custom Styles" files={radioGroupContent.customStyles} />

### Custom Implementation

In case you need to customize the radio group even further, you can use the `useRadio` hook to create your own implementation.

<CodeDemo title="Custom Implementation" files={radioGroupContent.customImpl} />

<Spacer y={4} />

## Data Attributes

- RadioGroup has the following attributes on the `base` element:

  - **data-orientation**:
    The orientation of the radio group. Based on `orientation` prop.

- Radio has the following attributes on the `base` element:

  - **data-selected**:
    When the radio is checked. Based on `isSelected` prop.
  - **data-pressed**:
    When the radio is pressed. Based on [usePress](https://react-spectrum.adobe.com/react-aria/usePress.html).
  - **data-invalid**:
    When the radio is invalid. Based on `validationState` prop.
  - **data-readonly**:
    When the radio is readonly. Based on `isReadOnly` prop.
  - **data-hover-unselected**:
    When the radio is being hovered and unchecked. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html).
  - **data-hover**:
    When the radio is being hovered. Based on [useHover](https://react-spectrum.adobe.com/react-aria/useHover.html).
  - **data-focus**:
    When the radio is being focused. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
  - **data-focus-visible**:
    When the radio is being focused with the keyboard. Based on [useFocusRing](https://react-spectrum.adobe.com/react-aria/useFocusRing.html).
  - **data-disabled**:
    When the radio is disabled. Based on `isDisabled` prop.

<Spacer y={4} />

## Accessibility

- Radio groups are exposed to assistive technology via ARIA.
- Each radio is built with a native HTML `<input>` element, which can be optionally visually hidden to allow custom styling.
- Full support for browser features like form autofill.
- Keyboard event support for arrows keys.
- Keyboard focus management and cross browser normalization.
- Group and radio labeling support for assistive technology.

<Spacer y={4} />

## API

### RadioGroup Props

<APITable
  data={[
    {
      attribute: "children",
      type: "ReactNode | ReactNode[]", 
      description: "The list of radio elements.",
      default: "-"
    },
    {
      attribute: "label",
      type: "ReactNode",
      description: "The label of the radio group.",
      default: "-"
    },
    {
      attribute: "size", 
      type: "sm | md | lg",
      description: "The size of the radios.",
      default: "md"
    },
    {
      attribute: "color",
      type: "default | primary | secondary | success | warning | danger",
      description: "The color of the radios.",
      default: "primary"
    },
    {
      attribute: "orientation",
      type: "horizontal | vertical",
      description: "The orientation of the radio group.",
      default: "vertical"
    },
    {
      attribute: "name",
      type: "string",
      description: "The name of the RadioGroup, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#name_and_radio_buttons).",
      default: "-"
    },
    {
      attribute: "value",
      type: "string[]",
      description: "The current selected value. (controlled)",
      default: "-"
    },
    {
      attribute: "defaultValue",
      type: "string[]", 
      description: "The default selected value. (uncontrolled)",
      default: "-"
    },
    {
      attribute: "description",
      type: "ReactNode",
      description: "Radio group description.",
      default: "-"
    },
    {
      attribute: "errorMessage",
      type: "ReactNode | ((v: ValidationResult) => ReactNode)",
      description: "Radio group error message.",
      default: "-"
    },
    {
      attribute: "validate",
      type: "(value: string) => ValidationError | true | null | undefined",
      description: "Validate input values when committing (e.g. on blur), returning error messages for invalid values. Validation errors are displayed upon form submission if `validationBehavior` is set to `native`. For real-time validation, use the `isInvalid` prop.",
      default: "-"
    },
    {
      attribute: "validationBehavior",
      type: "native | aria",
      description: "Whether to use native HTML form validation or ARIA validation. When wrapped in a Form component, the default is `aria`. Otherwise, the default is `native`.",
      default: "native"
    },
    {
      attribute: "isDisabled",
      type: "boolean",
      description: "Whether the radio group is disabled.",
      default: "false"
    },
    {
      attribute: "isRequired",
      type: "boolean", 
      description: "Whether user checkboxes are required on the input before form submission.",
      default: "false"
    },
    {
      attribute: "isReadOnly",
      type: "boolean",
      description: "Whether the checkboxes can be selected but not changed by the user.",
      default: "-"
    },
    {
      attribute: "isInvalid",
      type: "boolean",
      description: "Whether the radio group is invalid.",
      default: "false"
    },
    {
      attribute: "validationState",
      type: "valid | invalid",
      description: "Whether the inputs should display its \"valid\" or \"invalid\" visual styling. (Deprecated) use isInvalid instead.",
      default: "false"
    },
    {
      attribute: "disableAnimation",
      type: "boolean",
      description: "Whether the animation should be disabled.",
      default: "false"
    },
    {
      attribute: "classNames",
      type: "Partial<Record<\"base\" | \"wrapper\" | \"label\", string>>",
      description: "Allows to set custom class names for the radio group slots.",
      default: "-"
    }
  ]}
/>

### RadioGroup Events

<APITable
  data={[
    {
      attribute: "onChange",
      type: "React.ChangeEvent<HTMLInputElement>",
      description: "Handler that is called when the element's value changes. You can pull out the new value by accessing event.target.value (string).",
      default: "-"
    },
    {
      attribute: "onValueChange",
      type: "((value: string) => void)",
      description: "Handler that is called when the value changes.",
      default: "-"
    }
  ]}
/>

### Radio Props

<APITable
  data={[
    {
      attribute: "children",
      type: "ReactNode",
      description: "The label of the radio.",
      default: "-"
    },
    {
      attribute: "label",
      type: "ReactNode",
      description: "The label of the radio.",
      default: "-"
    },
    {
      attribute: "size",
      type: "sm | md | lg",
      description: "The size of the radio.",
      default: "md"
    },
    {
      attribute: "color",
      type: "default | primary | secondary | success | warning | danger",
      description: "The color of the radio.",
      default: "primary"
    },
    {
      attribute: "description",
      type: "ReactNode",
      description: "A description for the field. Provides a hint such as specific requirements for what to choose.",
      default: "-"
    },
    {
      attribute: "isDisabled",
      type: "boolean",
      description: "Whether the radio is disabled.",
      default: "false"
    },
    {
      attribute: "isRequired",
      type: "boolean",
      description: "Whether user checkboxes are required on the input before form submission.",
      default: "false"
    },
    {
      attribute: "isReadOnly",
      type: "boolean",
      description: "Whether the checkboxes can be selected but not changed by the user.",
      default: "-"
    },
    {
      attribute: "isInvalid",
      type: "boolean",
      description: "Whether the radio is invalid. This is based on the radio group validationState prop.",
      default: "false"
    },
    {
      attribute: "disableAnimation",
      type: "boolean",
      description: "Whether the animation should be disabled.",
      default: "false"
    },
    {
      attribute: "classNames",
      type: "Partial<Record<\"base\" | \"wrapper\" | \"labelWrapper\" | \"label\" | \"control\" | \"description\", string>>",
      description: "Allows to set custom class names for the radio slots.",
      default: "-"
    }
  ]}
/>
